﻿namespace Sai.Core
{
	using System;
	using System.Collections.Generic;
	using System.Collections.ObjectModel;
	using System.Linq;
	using System.Xml;
	using System.Xml.Serialization;

	/// <summary>
	/// Represents one version of a product including all the assemblies. 
	/// </summary>
	[XmlRoot("Release")]
	public class ProductRelease
	{
		public ProductRelease()
		{
			this.AllAssemblies = new List<ProductAssembly>();
		}

		public ProductRelease(string versionName, string folder)
			: this()
		{
			this.VersionName = versionName;
			this.Folder = folder;
		}

		public ProductRelease(string versionName, IList<ProductAssembly> assemblies)
		{
			this.VersionName = versionName;
			this.AllAssemblies = new List<ProductAssembly>(assemblies);
		}

		[XmlAttribute("Version")]
		public string VersionName { get; set; }

		[XmlAttribute("Folder")]
		public string Folder { get; set; }

		[XmlArray("Assemblies")]
		[XmlArrayItem("Assembly", typeof(ProductAssembly))]
		public Collection<ProductAssembly> Assemblies
		{
			get
			{
				return new Collection<ProductAssembly>(this.AllAssemblies);
			}
		}

		private List<ProductAssembly> AllAssemblies { get; set; }

		public void Add(ProductAssembly assembly)
		{
			this.AllAssemblies.Add(assembly);
		}

		public ProductAssembly AssemblyMatching(ProductAssembly info)
		{
			return this.AllAssemblies.FirstOrDefault(x => x.Name == info.Name);
		}

		public ReadOnlyCollection<ProductAssembly> CommonAssemblies(ProductRelease other)
		{
			var intersection = this.AllAssemblies.Intersect(other.AllAssemblies, new ProductAssemblyNameComparer());

			return new ReadOnlyCollection<ProductAssembly>(intersection.ToList());
		}

		public ReadOnlyCollection<ProductAssembly> AddedAssemblies(ProductRelease other)
		{
			var newOnly = this.AllAssemblies.Except(other.AllAssemblies, new ProductAssemblyNameComparer());

			return new ReadOnlyCollection<ProductAssembly>(newOnly.ToList());
		}

		public ReadOnlyCollection<ProductAssembly> RemovedAssemblies(ProductRelease other)
		{
			var oldOnly = other.AllAssemblies.Except(this.AllAssemblies, new ProductAssemblyNameComparer());

			return new ReadOnlyCollection<ProductAssembly>(oldOnly.ToList());
		}

		public ReadOnlyCollection<AssemblyComparison> CompareTo(ProductRelease other)
		{
			var results = new List<AssemblyComparison>();

			foreach (var added in this.AddedAssemblies(other))
			{
				results.Add(new AssemblyComparison
				{
					New = added,
					Result = AssemblyComparisonResult.Added
				});
			}

			foreach (var removed in this.RemovedAssemblies(other))
			{
				results.Add(new AssemblyComparison
				{
					Old = removed,
					Result = AssemblyComparisonResult.Removed
				});
			}

			foreach (var common in this.CommonAssemblies(other))
			{
				var oldVersion = other.AssemblyMatching(common);
				var newVersion = this.AssemblyMatching(common);

				if (oldVersion == newVersion)
				{
					// if there's an exact match in all respects
					// this may be the case if we're using 
					// the same version of a third party library 
					results.Add(new AssemblyComparison
					{
						New = newVersion,
						Old = oldVersion,
						Result = AssemblyComparisonResult.NoChange
					});
				}
				else
				{
					// most common files need to be analysed 
					// further to check for API changes...
					results.Add(new AssemblyComparison
					{
						New = newVersion,
						Old = oldVersion,
						Result = AssemblyComparisonResult.Indeterminate
					});
				}
			}

			return new ReadOnlyCollection<AssemblyComparison>(results);
		}
	}
}
